{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/obss/BIOBSS/blob/main/examples/respiratory_analysis.ipynb)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__BIOBSS - Respiratory Analysis__\n",
    "\n",
    "_This notebook includes guidelines to help using BIOBSS for respiratory signal extraction and respiratory rate estimation from PPG signal._"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__To run this notebook in Colab:__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%bash\n",
    "git clone https://github.com/obss/biobss.git\n",
    "cd biobss\n",
    "pip install ."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install -U matplotlib\n",
    "import shutil\n",
    "from biobss import FIXTURES_ROOT\n",
    "shutil.move(\"/content/biobss/sample_data\",FIXTURES_ROOT.parent)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__Import required packages:__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import biobss\n",
    "import os\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Table of Contents\n",
    "1.[PPG Sample Data](#sampledata)<br>\n",
    "2.[Preprocessing](#resp_pre)<br>\n",
    "3.[Extraction of Respiratory Signals](#resp_sig)<br>\n",
    "4.[Plotting](#resp_plot)<br>\n",
    "5.[Respiratory Signal Filtering](#filt_resp)<br>\n",
    "6.[Calculation of Respiratory Quality Indices](#resp_rqi)<br>\n",
    "7.[Respiratory Rate Estimation](#resp_rr)<br>\n",
    "8.[Fusion of Respiratory Rates](#resp_fusion)<br>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this tutorial notebook, PPG sample data is used for respiratory analysis. Note that, the same procedures can be applied on ECG sample data."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### __PPG Sample Data__\n",
    "<a id=\"sampledata\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PPG sample data is provided as a csv file in BIOBSS\\sample data. The data file contains one PPG segment of 60-seconds length. The sampling rate of the signal is 64 Hz."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Load the sample data\n",
    "data, info = biobss.utils.load_sample_data(data_type='PPG_long')\n",
    "sig = np.asarray(data['PPG'])\n",
    "fs = info['sampling_rate']\n",
    "L = info['signal_length']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### __Preprocessing__\n",
    "<a id=\"resp_pre\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For respiratory rate estimation, the PPG signal should be filtered before extracting respiratory signal(s). The __resp_preprocess__ module includes functions with predefined filtering parameters to eliminate very high and very low frequencies."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#ELiminate very low frequencies. 5th order Butterworth highpass filter with 0.0665 Hz (4 bpm) cutoff frequency.\n",
    "filt_sig=biobss.resptools.elim_vlf(sig, sampling_rate=fs)\n",
    "#ELiminate very low frequencies. 5th order Butterworth lowpass filter with 0.5833 (35 bpm) cutoff frequency.\n",
    "filt_sig=biobss.resptools.elim_vhf(sig, sampling_rate=fs)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For the extraction of respiratory signals from modulations in the PPG signal, peak and trough locations are needed. To be able to calculate the modulations, number of peaks and troughs should match and the locations should be in order. Thus, peak control procedure should also be applied by setting __correct_peaks__ as True."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Peak detection\n",
    "info=biobss.ppgtools.ppg_detectpeaks(sig=sig, sampling_rate=fs, method='peakdet', delta=0.01, correct_peaks=True)\n",
    "locs_peaks=info['Peak_locs']\n",
    "peaks=sig[locs_peaks]\n",
    "locs_onsets=info['Trough_locs']\n",
    "onsets=sig[locs_onsets]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### __Extraction of Respiratory Signals__\n",
    "<a id=\"resp_sig\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The ___extract_resp_sig___ function can be used to extract respiratory signals based on three modulation types which are Amplitude Modulation (AM), Frequency Modulation (FM) and Baseline Wander (BW). The function returns a dictionary including amplitude values and corresponding sample numbers of respiratory signals for the selected modulation types."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Extract the respiratory signal related to 'AM','FM' and'BW' from the PPG signal.\n",
    "\n",
    "info=biobss.resptools.extract_resp_sig(sig=sig, peaks_locs=locs_peaks, troughs_locs=locs_onsets, sampling_rate=fs,mod_type=['AM','FM','BW'],resampling_rate=10)\n",
    "\n",
    "y_am=info['am_y']\n",
    "x_am=info['am_x']\n",
    "\n",
    "y_fm=info['fm_y']\n",
    "x_fm=info['fm_x']\n",
    "\n",
    "y_bw=info['bw_y']\n",
    "x_bw=info['bw_x']"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### __Plotting__\n",
    "<a id=\"resp_plot\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "BIOBSS provides plotting functions specific to each signal type. In order to plot respiratory signals, ___plot_resp___ function can be used. The signals and peaks should be provided as dictionaries, and the keys should be selected properly as shown below. The plots can be generated either using __Matplotlib__ or __Plotly__. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Generate inputs as dictionaries\n",
    "signals = {'AM': {'x': x_am, 'y':y_am}, 'FM': {'x': x_fm, 'y':y_fm}, 'BW': {'x': x_bw, 'y':y_bw}}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Plot respiratory signals using Matplotlib\n",
    "biobss.resptools.plot_resp(signals=signals, sampling_rate=fs, show_peaks=False, figsize=(10,5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Plot respiratory signals using Plotly\n",
    "biobss.resptools.plot_resp(signals=signals, sampling_rate=fs, method='plotly', show_peaks=False, width=800, height=400)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### __Respiratory Signal Filtering__\n",
    "<a id=\"filt_resp\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If required, ___filter_resp_sig___ function can be used to filter respiratory signal using __rsp_clean__ function from __Neurokit2__ package."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Filter respiratory signals\n",
    "info=biobss.resptools.filter_resp_sig(resampling_rate=10, am_sig=y_am, am_x=x_am, fm_sig=y_fm, fm_x=x_fm, bw_sig=y_bw, bw_x=x_bw)\n",
    "\n",
    "y_am=info['am_sig']\n",
    "x_am=info['am_x']\n",
    "\n",
    "y_fm=info['fm_sig']\n",
    "x_fm=info['fm_x']\n",
    "\n",
    "y_bw=info['bw_sig']\n",
    "x_bw=info['bw_x']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### __Calculation of Respiratory Quality Indices__\n",
    "<a id=\"resp_rqi\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Quality of respiratory signals extracted from modulations in PPG signal depends on quality of PPG signal, other artifacts that may be in the same frequency range with the respiratory activity and demographic features of the subject. Thus, respiration rates may differ in accuracy for each respiratory signal. Respiratory quality indices (RQI) can be defined as a measure of quality of respiratory signals. In the literature, there are several respiratory quality indices defined for this purpose. BIOBSS library provides the function ___calc_rqi___ to calculate 'Autocorrelation' and 'Hjorth' respiratory quality indices."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Calculate respiratory quality indices. Note that, this function should be applied for each modulation type seperately.\n",
    "#The function returns a dictionary of respiratory quality indices.\n",
    "rqi_am=biobss.resptools.calc_rqi(y_am,resampling_rate=10)\n",
    "rqi_fm=biobss.resptools.calc_rqi(y_fm,resampling_rate=10)\n",
    "rqi_bw=biobss.resptools.calc_rqi(y_bw,resampling_rate=10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### __Respiratory Rate Estimation__\n",
    "<a id=\"resp_rr\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Respiratory rate can be estimated from respiratory signals using various methods. The ___estimate_rr___ function provides two options which are 'peakdet' and 'xcorr' methods. The 'peakdet' method calculates respiratory rate by detecting peaks of respiratory signal and calculating the peak to peak intervals. The method __xcorr__ is imported from __Neurokit2__ library. It calculates cross-correlations between the changes in respiration with a bank of sinusoids of different frequencies to identify the principal frequency of oscillation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Estimate respiratory rate using 'peakdet' method. Note that, this function should be applied for each modulation type seperately.\n",
    "rr_am=biobss.resptools.estimate_rr(y_am,10,method='xcorr')\n",
    "rr_fm=biobss.resptools.estimate_rr(y_fm,10,method='xcorr')\n",
    "rr_bw=biobss.resptools.estimate_rr(y_bw,10,method='peakdet')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### __Fusion of Respiratory Rates__\n",
    "<a id=\"resp_fusion\"></a>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Estimated respiratory rates can be fused into a single value using ___fuse_rr___ function. The function provides two options for fusion method which are 'SmartFusion' and 'QualityFusion'. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "'SmartFusion' calculates the average of respiratory rates considering the standart deviation of the estimates. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Fuse the respiratory rates using 'SmartFusion' method.\n",
    "rr_fused=biobss.resptools.fuse_rr(rr_est=[rr_am, rr_fm, rr_bw], fusion_method='SmartFusion')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "'QualityFusion' takes respiratory quality indices into account. This time, rqi values should also be provided and the elements of rqi and rr_est arrays should be in the same order. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Fuse the respiratory rates using 'QualityFusion' method.\n",
    "#RQIs (array) should be provided as a keyworded argument. \n",
    "#The order of elements in the array should match with the order RRs of different modulation types.\n",
    "rqi=[rqi_am['hjorth'],rqi_fm['hjorth'],rqi_bw['hjorth']]\n",
    "rr_fused=biobss.resptools.fuse_rr(rr_est=[rr_am, rr_fm, rr_bw], rqi=rqi, fusion_method='QualityFusion')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.8.5 64-bit ('biolib')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.16"
  },
  "orig_nbformat": 4,
  "vscode": {
   "interpreter": {
    "hash": "3a4bcfb23c7e6ad66c655087280fa9f4d0273121ae7909f7735d1e02563a2438"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
